/*
 * MyPreferenceManager.java
 *
 * Created on December 31, 2005, 1:16 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package net.jxta.myjxta.util.preferences;

import net.jxta.ext.config.Resource;
import net.jxta.ext.config.ResourceNotFoundException;
import net.jxta.logging.Logging;
import net.jxta.myjxta.MyJXTA;
import net.jxta.myjxta.util.Env;

import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author polo
 */
public class MyPreferenceManager {

    private Resource res = null;
    private static final Logger LOG = Logger.getLogger(MyPreferenceManager.class.getName());
    public static final String ROOT = "myjxtaPrefs";
    public static final String PREFERENCES = "preferences";
    public static final String CATEGORY = "category";
    public static final String PREFERENCE_SET = "preferenceSet";
    public static final String PREFERENCE = "preference";
    public static final String SEP = "/";
    public static final String ATTRIBUTE_NAME = "@name";
    public static final String ATTRIBUTE_DESCRIPTION = "@description";
    public static final String ATTRIBUTE_VISIBLE = "@visible";
    public static final String ATTRIBUTE_LABEL = "@label";
    public static final String ATTRIBUTE_VALUE = "@value";
    public static final String ATTRIBUTE_ENABLE = "@enable";
    public static final String ATTRIBUTE_ICON = "@icon";
    /*public static final String ATTRIBUTE_ICON = "TreeTable.Preference";*/
    public static final String ATTRIBUTE_TOOLTIP = "@tooltip";
    public static final String ATTRIBUTE_CLASS = "@class";
    public static final String ATTRIBUTE_DIMENSION = "@dimension";

    public static final String DEFAULT_PREFSET_CLASS_NAME = DefaultPreferenceSet.class.getName();

    public static final String CATEGORY_GENERAL = "General";
    public static final String CATEGORY_PLUGINS = "plugins";

    public static String PREFSET_LEGACY = null;

// todo set the defalt icon

    public static final String DEFAULT_ICON = "TreeTable.PeerPresent";

    public static final String PREFERENCESET_LEGACY = "General.Legacy";
    public static final String PREFERENCESET_PLUGINPATHS = "plugins.paths";
    public static final String PREFERENCE_CHIME = "General.Legacy.Chime";
    public static final String PREFERENCE_INSTALL = "plugins.paths.install";

    private List<String> categoryList = null;
    private LinkedHashMap<String, PreferenceSet> prefSetTable = null;


    static private MyPreferenceManager myinstance = null;

    static public MyPreferenceManager getInstance() {

        if (myinstance == null) {

            myinstance = new MyPreferenceManager();
        }
        return myinstance;
    }

    public MyJXTA getMyJxta() {
        return MyJXTA.getTheInstance();
    }


    public static String buildPath() {
        return buildPath(null, null, null, null);
    }

    public static String buildPath(String categoryName) {
        return buildPath(categoryName, null, null, null);
    }

    public static String buildPath(String categoryName, String preferenceSetName) {
        return buildPath(categoryName, preferenceSetName, null, null);
    }

    public static String buildPath(String categoryName, String preferenceSetName, String preferenceName) {
        return buildPath(categoryName, preferenceSetName, preferenceName, null);
    }

    public static String buildPath(String categoryName, String preferenceSetName, String preferenceName, String attr) {
        StringBuffer rtn = new StringBuffer(ROOT);
        rtn.append(SEP);
        rtn.append(PREFERENCES);
        if (categoryName != null) {
            rtn.append(SEP);
            rtn.append(CATEGORY);
            rtn.append("[");
            rtn.append(ATTRIBUTE_NAME);
            rtn.append("=\"");
            rtn.append(categoryName);
            rtn.append("\"]");
            if (preferenceSetName != null) {
                rtn.append(SEP);
                rtn.append(PREFERENCE_SET);
                rtn.append("[");
                rtn.append(ATTRIBUTE_NAME);
                rtn.append("=\"");
                rtn.append(preferenceSetName);
                rtn.append("\"]");
                if (preferenceName != null) {
                    rtn.append(SEP);
                    rtn.append(PREFERENCE);
                    rtn.append("[");
                    rtn.append(ATTRIBUTE_NAME);
                    rtn.append("=\"");
                    rtn.append(preferenceName);
                    rtn.append("\"]");

                    if (attr != null) {
                        rtn.append(SEP);
                        rtn.append(attr);
                    }
                }
            }
        }
        return rtn.toString();

    }

    private Class loadClass(String className) {
        Class prefSetClass;
        try {

            prefSetClass = Class.forName(className);

        } catch (LinkageError le) {
            le.printStackTrace();
            prefSetClass = null;
            // xxx: todo: set status message here
        } catch (ClassNotFoundException cnfx) {
            //cnfx.printStackTrace();
            prefSetClass = null;
            // xxx: todo: set status message here
        }

        return prefSetClass;

    }

    public PreferenceSet instantiatePreferenceSet(String prefSetClassName) {

        Class prefSetClass;
        PreferenceSet prefSetInstance = null;
        StringBuffer errorMessage = new StringBuffer();
        /** Load class and test for correct superclass */
        prefSetClass = loadClass(prefSetClassName);

        if (prefSetClass == null) {
            prefSetClass = loadClass(DEFAULT_PREFSET_CLASS_NAME);
        }

        if (prefSetClass != null) {

            if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
                LOG.fine("class.forName " + prefSetClass.getName());
            }

            Class superClass = prefSetClass.getSuperclass();

            if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
                LOG.fine("superClass " + superClass.getName());
            }

            // if superclass is good then instantiate PreferenceSet class
            if (superClass.getName().equals(PreferenceSet.class.getName())) {

                if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
                    LOG.fine("superClass is good");
                }


                try {


                    Class customPreferenceSet = Class.forName(prefSetClass.getName());

                    if (PreferenceSet.class.isAssignableFrom(customPreferenceSet)) {

                        prefSetInstance = (PreferenceSet) customPreferenceSet.newInstance();


                    }

                } catch (ClassCastException ccx) {
                    ccx.printStackTrace();
                    errorMessage.append("ClassCast Error in Preference Set Class Instantiation\n\r");
                    errorMessage.append("Using Default Preference Set Class");
                    prefSetInstance = instantiatePreferenceSet(DEFAULT_PREFSET_CLASS_NAME);
                    if (prefSetInstance != null && (prefSetInstance instanceof DefaultPreferenceSet)) {
                        ((DefaultPreferenceSet) prefSetInstance).setErrorMessage(errorMessage.toString());
                    }
                    // xxx: todo: set status message here
                } catch (InstantiationException ix) {
                    ix.printStackTrace();
                    errorMessage.append("Instantiation Error in Preference Set Class Instantiation\n\r");
                    errorMessage.append("Using Default Preference Set Class");
                    prefSetInstance = instantiatePreferenceSet(DEFAULT_PREFSET_CLASS_NAME);
                    if (prefSetInstance != null && prefSetInstance instanceof DefaultPreferenceSet) {
                        ((DefaultPreferenceSet) prefSetInstance).setErrorMessage(errorMessage.toString());
                    }
                    // xxx: todo: set status message here
                } catch (IllegalAccessException iax) {
                    iax.printStackTrace();
                    errorMessage.append("IllegalAccess Error in Preference Set Class Instantiation\n\r");
                    errorMessage.append("Using Default Preference Set Class");
                    prefSetInstance = instantiatePreferenceSet(DEFAULT_PREFSET_CLASS_NAME);
                    if (prefSetInstance != null && prefSetInstance instanceof DefaultPreferenceSet) {
                        ((DefaultPreferenceSet) prefSetInstance).setErrorMessage(errorMessage.toString());
                    }
                    // xxx: todo: set status message here
                } catch (LinkageError le) {
                    le.printStackTrace();
                    errorMessage.append("Linkage Error in Preference Set Class Instantiation\n\r");
                    errorMessage.append("Using Default Preference Set Class");
                    prefSetInstance = instantiatePreferenceSet(DEFAULT_PREFSET_CLASS_NAME);
                    if (prefSetInstance != null && prefSetInstance instanceof DefaultPreferenceSet) {
                        ((DefaultPreferenceSet) prefSetInstance).setErrorMessage(errorMessage.toString());
                    }
                    // xxx: todo: set status message here
                } catch (ClassNotFoundException cnfx) {
                    cnfx.printStackTrace();
                    errorMessage.append("ClassNotFound Error in Preference Set Class Instantiation\n\r");
                    errorMessage.append("Using Default Preference Set Class");
                    prefSetInstance = instantiatePreferenceSet(DEFAULT_PREFSET_CLASS_NAME);
                    if (prefSetInstance != null && prefSetInstance instanceof DefaultPreferenceSet) {
                        ((DefaultPreferenceSet) prefSetInstance).setErrorMessage(errorMessage.toString());
                    }
                    // xxx: todo: set status message here
                }
            }
        }

        return prefSetInstance;
    }


    public PreferenceSet getPreferenceSet(String categoryName, String preferenceSetName) {
        String path = buildPath(categoryName, preferenceSetName);
        return getPreferenceSet(path);
    }

    private PreferenceSet getPreferenceSet(String path) {

        return this.prefSetTable.get(path);

//getClass attr
//        String className = getAttribute(path, ATTRIBUTE_CLASS);

        //      PreferenceSet prefSet = instantiatePreferenceSet(className);
        //inject root path
        //    prefSet.setRootPath(path);

        //  return prefSet;
    }


    public PreferenceSet getPluginPreferenceSet(String pluginPrefSetName) {
        return getPreferenceSet(buildPath(CATEGORY_PLUGINS, pluginPrefSetName));
    }

    public String toString() {
        return res.toString();
    }

    public void setPreference(String path, String value) {
        path = addLeadingSlash(path);
        res.set(path, value);
    }

    public String getPreference(String name) {
        name = addLeadingSlash(name);
        return res.get(name);
    }

    public String getAttribute(String prefName, String attr) {
        String rtn;

        prefName = addLeadingSlash(prefName);
        if (attr.startsWith("@")) {
            rtn = prefName + SEP + attr;
        } else {
            rtn = prefName + SEP + "@" + attr;
        }

        return res.get(rtn);
    }

    private String addLeadingSlash(String prefName) {
        if (!prefName.startsWith("/"))
            prefName = "/" + prefName;
        return prefName;
    }

    public List getAttributes(String name, String attr) {
        return getPreferences(name, attr);
    }

    public List getPreferences(String name, String attr) {
        String rtn = name;
        name = addLeadingSlash(name);
        if (attr != null) {
            if (attr.startsWith("@")) {
                rtn = name + SEP + attr;
            } else {
                rtn = name + SEP + "@" + attr;
            }
        }


        return res.getAll(rtn);
    }


    public void shutdown() {

        save();


    }

    public void save() {
        Env.savePreferences(res.toString());
    }


    /**
     * Creates a new instance of MyPreferenceManager
     */
    private MyPreferenceManager() {

        PREFSET_LEGACY = buildPath(CATEGORY_GENERAL, PREFERENCESET_LEGACY);


        LOG.setLevel(Level.FINE);
        try {

            res = new Resource(MyPreferenceManager.ROOT);

            try {
                //res.load(new URL("file:///home/polo/dev/prefs.xml"));
                res.load(Env.getPreferences());

            } catch (ResourceNotFoundException ex) {
                ex.printStackTrace();
            }
        } catch (Exception x) {
            x.printStackTrace();
        }


        prefSetTable = new LinkedHashMap<String, PreferenceSet>();


    }

    public void init() {
        scanCategoryList(); // initializes the preferencsets
    }

    protected List<String> getCategorys() {
        if (categoryList == null) {
            categoryList = new ArrayList<String>();
            scanCategoryList();
        }
        return categoryList;
    }

    private void scanCategoryList() {
        if (categoryList == null) {
            categoryList = new ArrayList<String>();
        }

        List l = getAttributes(ROOT + SEP + PREFERENCES + SEP + CATEGORY, ATTRIBUTE_NAME);

        for (Object aL : l) {
            String categoryPath = buildPath((String) aL);
            if (!categoryList.contains(categoryPath)) {
                categoryList.add(categoryPath);
                setupPreferenceSets(categoryPath);
            }
        }

        for (String category : categoryList) {
            if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
                LOG.fine("category " + category);
            }
        }
        for (String prefSet : prefSetTable.keySet()) {
            if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
                LOG.fine("prefset " + prefSet);
            }
        }
    }


    protected HashMap<String, PreferenceSet> getPreferenceSets() {
        return prefSetTable;
    }

    public Hashtable<String, PreferenceSet> getPreferenceSets(String categoryPath) {
        Hashtable<String, PreferenceSet> table = new Hashtable<String, PreferenceSet>();
        for (Iterator<String> i = prefSetTable.keySet().iterator(); i != null && i.hasNext();) {
            String path = i.next();
            if (path.startsWith(categoryPath)) {
                table.put(path, prefSetTable.get(path));
            }
        }
        return table;
    }

    private void setupPreferenceSets(String categoryPath) {
        String pathToPreferenceSet = categoryPath + SEP + PREFERENCE_SET;
        List l = getAttributes(pathToPreferenceSet, ATTRIBUTE_NAME);
        Iterator itr = l.iterator();
        String categoryName = getAttribute(categoryPath, ATTRIBUTE_NAME);
        while (itr.hasNext()) {
            String prefSetPath = buildPath(categoryName, (String) itr.next());
            if (Boolean.valueOf(getAttribute(prefSetPath, ATTRIBUTE_ENABLE))) {
                if (!prefSetTable.containsKey(prefSetPath)) { //if its not already in the list
                    String prefSetClassName = getAttribute(prefSetPath, ATTRIBUTE_CLASS);
                    PreferenceSet prefSet = instantiatePreferenceSet(prefSetClassName);
                    prefSet.setRootPath(prefSetPath);
                    prefSet.init();
                    prefSetTable.put(prefSetPath, prefSet);
                }
            }
        }
    }

    public static Dimension stringToDimension(String s) {


        int w = 0;
        int h = 0;
        Dimension rtn = null;

        if (s != null) {

            String[] tmp = s.split("x");

            if (tmp.length == 2) {

                try {

                    w = Integer.valueOf(tmp[0].trim());

                    h = Integer.valueOf(tmp[1].trim());

                } catch (NumberFormatException nfx) {
                    nfx.printStackTrace();
                }
            }
            rtn = new Dimension(w, h);
        }
        return rtn;

    }

    public static String dimensionToString(Dimension d) {

        return (d != null) ?
                String.valueOf(String.valueOf((int) d.getWidth()) +
                        " x " +
                        String.valueOf((int) d.getHeight())) : null;
    }
}
